import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @projectName: java-daily-questions
 * @package: PACKAGE_NAME
 * @className: ProviderAndConsumer
 * @author: ZM
 * @description: 生产者与消费者
 * @date: 2021/8/26 15:13
 * @version: 1.0
 */
public class ProviderAndConsumer {

    public static void main(String[] args) {
        // 两个生产者，一个消费者
        Queue queue = new Queue();
        Thread provider1 = new Thread(new Provider(queue));
        provider1.setName("Provider1");
        provider1.start();

        Thread provider2 = new Thread(new Provider(queue));
        provider2.setName("Provider2");
        provider2.start();

        Thread consumer = new Thread(new Consumer(queue));
        consumer.setName("Consumer");
        consumer.start();
    }

    static class Queue {
        private final LinkedList<Integer> queue = new LinkedList<>(Collections.singleton(6));
        private volatile int size = 0;

        // 初始化锁和两个Condition
        private final ReentrantLock lock = new ReentrantLock();
        public Condition pCondition = lock.newCondition();
        public Condition cCondition = lock.newCondition();

        public void lock() {
            lock.lock();
        }

        public void unLock() {
            lock.unlock();
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public boolean isFull() {
            return size == 6;
        }

        public void put(int value, String threadName) throws InterruptedException {
            try {
                lock();
                if (isFull()){
                    // 队列满了让生产者等待
                    pCondition.await();
                }
                queue.add(value);
                size = queue.size();
                // 生产完唤醒消费者
                cCondition.signalAll();
            } finally {
                System.out.println(threadName +"-> put after: " + printQueue(queue));
                unLock();
            }
        }

        public int pop() throws InterruptedException {
            try {
                lock();
                // 队列空了, 就让消费者等待
                if (isEmpty()){
                    cCondition.await();
                }
                int value = queue.remove(0);
                size = queue.size();
                // 消费完通知生产者
                pCondition.signalAll();
                return value;
            } finally {
                System.out.println("take after: " + printQueue(queue));
                unLock();
            }
        }

    }

    static class Provider implements Runnable {

        Queue queue;

        public Provider(Queue queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    System.out.println("---productionTime---");
                    TimeUnit.SECONDS.sleep(2);
                    queue.put(new Random().nextInt(100), Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class Consumer implements Runnable {

        Queue queue;

        public Consumer(Queue queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    System.out.println("---consumptionTime---");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println("消费：" + queue.pop());
                    System.out.println();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static String printQueue(LinkedList<Integer> queue) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i : queue) {
            stringBuilder.append(i).append("->");
        }
        stringBuilder.append("null");
        return stringBuilder.toString();
    }
}
